﻿using System.Collections.Generic;
using Cms.Application.Admin.Presenters;
using Cms.Application.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Cms.Domain;
using Cms.Application.Admin.Views;
using Moq;

namespace Cms.Tests.Unit.Application.Admin.Presenters
{
    [TestClass]
    public class ExplorerPresenterTests : BasePresenterTests
    {
        private Site _site;
        private Administrator _admin;
        private Mock<IExplorerView> _viewMock;
        private ExplorerPresenter _presenter;
        private Folder _folder1;
        private Folder _folder2;
        private Folder _folder3;
        private Folder _folder4;
        private Folder _folder5;
        private Folder _folder6;
        private Page _page1;
        private ApplicationPage _page2;
        private Page _page3;

        private void SetupStructure()
        {
            // Setup a structure that covers all test cases like this:
            //
            // Root
            //     Folder1 (F1)
            //         Page1 (P1)
            //     Folder2 (F2)
            //         Folder3 (F3)
            //             Folder5 (F5)
            //         Folder4 (F4)
            //             Page2 (A2)
            //         Folder1 (F6)
            //     Page1 (P3)

            _folder1 = new Folder { ID = 1, Site = _site, Name = "Folder1" };
            _folder2 = new Folder { ID = 2, Site = _site, Name = "Folder2" };
            _folder3 = new Folder { ID = 3, Site = _site, Name = "Folder3" };
            _folder4 = new Folder { ID = 4, Site = _site, Name = "Folder4" };
            _folder5 = new Folder { ID = 5, Site = _site, Name = "Folder5" };
            _folder6 = new Folder { ID = 6, Site = _site, Name = "Folder1" }; // Note the duplicate name
            FolderRepositoryMock.Setup(f => f.GetFolder(1)).Returns(_folder1);
            FolderRepositoryMock.Setup(f => f.GetFolder(2)).Returns(_folder2);
            FolderRepositoryMock.Setup(f => f.GetFolder(3)).Returns(_folder3);
            FolderRepositoryMock.Setup(f => f.GetFolder(4)).Returns(_folder4);
            FolderRepositoryMock.Setup(f => f.GetFolder(5)).Returns(_folder5);
            FolderRepositoryMock.Setup(f => f.GetFolder(6)).Returns(_folder6);
            _site.Folders.Add(_folder1);
            _site.Folders.Add(_folder2);
            _site.Folders.Add(_folder3);
            _site.Folders.Add(_folder4);
            _site.Folders.Add(_folder5);
            _site.Folders.Add(_folder6);
            _page1 = new Page { ID = 1, Site = _site, Filename = "Page1.aspx" };
            _page2 = new ApplicationPage { ID = 2, Site = _site, Filename = "Page2.aspx" };
            _page3 = new Page { ID = 3, Site = _site, Filename = "Page1.aspx" }; // Note the duplicate name
            PageRepositoryMock.Setup(f => f.GetPage(1)).Returns(_page1);
            PageRepositoryMock.Setup(f => f.GetPage(2)).Returns(_page2);
            PageRepositoryMock.Setup(f => f.GetPage(3)).Returns(_page3);
            _site.Pages.Add(_page1);
            _site.Pages.Add(_page2);
            _site.Pages.Add(_page3);
            _folder1.Pages.Add(_page1);
            _page1.Folder = _folder1;
            _folder2.SubFolders.Add(_folder3);
            _folder3.Parent = _folder2;
            _folder3.SubFolders.Add(_folder5);
            _folder5.Parent = _folder3;
            _folder2.SubFolders.Add(_folder4);
            _folder4.Parent = _folder2;
            _folder2.SubFolders.Add(_folder6);
            _folder6.Parent = _folder2;
            _folder4.Pages.Add(_page2);
            _page2.Folder = _folder4;
        }

        private void Prepare()
        {
            InitRepositoryFactoryMock();
            _site = new Site { ID = 1, Domain = "www.fredium.com" };
            _admin = new Administrator { CurrentSite = _site };
            AdministratorGroup group = new AdministratorGroup { DefaultPageAccess = DefaultAccess.Allow };
            _admin.Group = group;
            AdministratorRepositoryMock.Setup(a => a.GetAdministrator(1)).Returns(_admin);
            SetupStructure();
            _viewMock = new Mock<IExplorerView>();
            _viewMock.SetupAllProperties();
            _viewMock.Setup(v => v.AdministratorID).Returns(1);
            _viewMock.Setup(v => v.Localization).Returns("en-US");
            _viewMock.Setup(v => v.ContextTranslationFile).Returns("Admin/Translations/Explorer.xml");
            // These methods are called by the FileReferencesManager and need to be set up
            ContentRepositoryMock.Setup(m => m.GetContentWithReferencesTo(It.IsAny<string>(), _site.ID)).Returns(new List<ContentTranslationVersion>());
            SiteRepositoryMock.Setup(m => m.GetSourceCodeWithReferencesTo(It.IsAny<string>(), _site.ID)).Returns(new List<SourceCodeVersion>());
            SiteRepositoryMock.Setup(m => m.GetRootFolders(_site)).Returns(_site.GetRootFolders());
            SiteRepositoryMock.Setup(m => m.GetRootFoldersForExplorer(_site)).Returns(_site.GetRootFolders());
        }

        [TestMethod]
        public void InitializeSelectedFolderID_AdministratorHasReferenceToExistingFolder_SelectedFolderSet()
        {
            Prepare();
            Folder folder22 = new Folder { ID = 22 };
            FolderRepositoryMock.Setup(f => f.GetFolder(22)).Returns(folder22);
            _admin.SelectedFolderID = 22;
            PageRepositoryMock.Setup(m => m.GetPages(1, It.IsAny<int>())).Returns(_site.Pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            Assert.AreEqual(22, _viewMock.Object.SelectedFolderID);
        }
        
        [TestMethod]
        public void InitializeSelectedFolderID_AdministratorHasNoReference_SelectedFolderSetToDefaultValue()
        {
            Prepare();
            PageRepositoryMock.Setup(m => m.GetPages(1, -1)).Returns(_site.Pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            Assert.AreEqual(-1, _viewMock.Object.SelectedFolderID);
        }
        
        [TestMethod]
        public void InitializeSelectedFolderID_FolderDoesNotExist_SelectedFolderSetToDefaultValue()
        {
            Prepare();
            Folder folder22 = new Folder { ID = 22 };
            FolderRepositoryMock.Setup(f => f.GetFolder(22)).Returns(folder22);
            _admin.SelectedFolderID = 666; // Doesn't exist!
            PageRepositoryMock.Setup(m => m.GetPages(1, It.IsAny<int>())).Returns(_site.Pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            Assert.AreEqual(-1, _viewMock.Object.SelectedFolderID);
        }

        [TestMethod]
        public void Initialize_CallsSetFolders()
        {
            Prepare();
            PageRepositoryMock.Setup(m => m.GetPages(1, It.IsAny<int>())).Returns(_site.Pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Verify(o => o.SetFolders(It.IsAny<string>(), It.IsAny<IList<Folder>>(), ""));
        }
        
        [TestMethod]
        public void Initialize_CallsOpenFolder_PagesSet()
        {
            Prepare();
            PageRepositoryMock.Setup(m => m.GetPages(1, -1)).Returns(_site.Pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Verify(o => o.SetPages(It.IsAny<IList<Page>>()));
        }
        
        [TestMethod]
        public void OpenFolder_SiteHasThreeRootPages_PagesSet()
        {
            Prepare();
            Page p1 = new Page { ID = 1, Site = _site };
            Page p2 = new Page { ID = 2, Site = _site };
            Page p3 = new Page { ID = 3, Site = _site };
            IList<Page> pages = new List<Page> { p1, p2, p3 };
            PageRepositoryMock.Setup(m => m.GetPages(_site.ID, _admin.SelectedFolderID)).Returns(pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object);
            _viewMock.Object.SelectedFolderID = -1;
            presenter.OpenFolder();
            _viewMock.Verify(o => o.SetPages(pages));
        }
        
        [TestMethod]
        public void Initialize_NoMasterTemplates_CallWarn()
        {
            Prepare();
            PageRepositoryMock.Setup(m => m.GetPages(1, It.IsAny<int>())).Returns(_site.Pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Verify(o => o.DisplayCreateMasterTemplateAlert());
        }
        
        [TestMethod]
        public void Initialize_NoDetailTemplates_CallWarn()
        {
            Prepare();
            MasterTemplate mt = new MasterTemplate { Site = _site };
            _site.MasterTemplates.Add(mt);
            PageRepositoryMock.Setup(m => m.GetPages(1, It.IsAny<int>())).Returns(_site.Pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Verify(o => o.DisplayCreateDetailTemplateAlert());
        }
        
        [TestMethod]
        public void Initialize_HasMasterTemplates_DontCallWarn()
        {
            Prepare();
            MasterTemplate mt = new MasterTemplate {Site = _site};
            _site.MasterTemplates.Add(mt);
            PageRepositoryMock.Setup(m => m.GetPages(1, -1)).Returns(_site.Pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Verify(o => o.DisplayCreateMasterTemplateAlert(), Times.Never());
        }
        
        [TestMethod]
        public void Initialize_HasDetailTemplates_DontCallWarn()
        {
            Prepare();
            DetailTemplate dt = new DetailTemplate { Site = _site };
            _site.DetailTemplates.Add(dt);
            PageRepositoryMock.Setup(m => m.GetPages(1, It.IsAny<int>())).Returns(_site.Pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Verify(o => o.DisplayCreateDetailTemplateAlert(), Times.Never());
        }

        [TestMethod]
        public void Move_GrandchildIntoRoot_Success()
        {
            Prepare();
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object);
            Mock<IDiskAccess> io = new Mock<IDiskAccess>();
            presenter.DiskAccess = io.Object;
            presenter.Move("F4", "domain");
            // Check relations
            Assert.IsTrue(_site.GetRootFolders().Contains(_folder4));
            Assert.IsFalse(_folder2.SubFolders.Contains(_folder4));
            Assert.IsNull(_folder4.Parent);
        }
        
        [TestMethod]
        public void Move_ApplicationPageIntoAnotherFolder_Success()
        {
            Prepare();
            PageRepositoryMock.Setup(m => m.GetPages(1, It.IsAny<int>())).Returns(_site.Pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object);
            Mock<IDiskAccess> io = new Mock<IDiskAccess>();
            presenter.DiskAccess = io.Object;
            presenter.Move("A2", "F1");
            // Check relations
            Assert.IsTrue(_folder1.Pages.Contains(_page2));
        }

        [TestMethod]
        public void Move_ParentFolderIntoGrandChild_Error()
        {
            Prepare();
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object) { DiskAccess = new Mock<IDiskAccess>().Object };
            presenter.Move("F2", "F5");
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(1)); // 1 = IsParentToTargetError
        }
        
        [TestMethod]
        public void Move_FolderNamingCollision_Error()
        {
            Prepare();
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object) { DiskAccess = new Mock<IDiskAccess>().Object };
            presenter.Move("F6", "domain");
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(2)); // 2 = MoveFolder_NameCollisionError
        }
        
        [TestMethod]
        public void Move_PageNamingCollision_Error()
        {
            Prepare();
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object) { DiskAccess = new Mock<IDiskAccess>().Object };
            presenter.Move("P1", "domain");
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(3)); // 3 = MovePage_NameCollisionError
        }
        
        [TestMethod]
        public void DeleteFolder_FolderNamingCollision_Error()
        {
            Prepare();
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object) { DiskAccess = new Mock<IDiskAccess>().Object };
            presenter.DeleteFolder(2); // Folder1 and Folder6 should collide
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(3)); // 3 = DeleteFolderError_FolderNameCollision
        }

        [TestMethod]
        public void DeleteFolder_PageNamingCollision_Error()
        {
            Prepare();
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object) { DiskAccess = new Mock<IDiskAccess>().Object };
            presenter.DeleteFolder(1); // Page1 and Page3 should collide
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(4)); // 4 = DeleteFolderError_PageFilenameCollision
        }

        [TestMethod]
        public void DeleteFolder_MoveSubFoldersUp_Success()
        {
            Prepare();
            PageRepositoryMock.Setup(m => m.GetPages(1, It.IsAny<int>())).Returns(_site.Pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object) { DiskAccess = new Mock<IDiskAccess>().Object };
            presenter.DeleteFolder(3);
            Assert.IsTrue(_folder2.SubFolders.Contains(_folder5));
            Assert.AreSame(_folder5.Parent, _folder2);
        }
        
        [TestMethod]
        public void DeleteFolder_MovePagesUp_Success()
        {
            Prepare();
            PageRepositoryMock.Setup(m => m.GetPages(1, It.IsAny<int>())).Returns(_site.Pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object) { DiskAccess = new Mock<IDiskAccess>().Object };
            presenter.DeleteFolder(4);
            Assert.IsTrue(_folder2.Pages.Contains(_page2));
            Assert.AreSame(_page2.Folder, _folder2);
        }

        [TestMethod]
        public void DeleteFolder_DeletedFolderWasSelected_ParentIsSelectedInstead()
        {
            Prepare();
            PageRepositoryMock.Setup(m => m.GetPages(1, 2)).Returns(_folder2.Pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object) { DiskAccess = new Mock<IDiskAccess>().Object };
            _viewMock.Object.SelectedFolderID = 4;
            presenter.DeleteFolder(4);
            Assert.AreEqual(2, _viewMock.Object.SelectedFolderID);
        }
        
        [TestMethod]
        public void GetPagePreviewUrl_Success()
        {
            Prepare();
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object) { DiskAccess = new Mock<IDiskAccess>().Object };
            Assert.AreEqual("http://www.fredium.com/Folder2/Folder4/Page2.aspx", presenter.GetPagePreviewUrl(2));
        }
        
        [TestMethod]
        public void DeletePage()
        {
            Prepare();
            PageRepositoryMock.Setup(m => m.GetPages(1, It.IsAny<int>())).Returns(_site.Pages);
            ExplorerPresenter presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object) { DiskAccess = new Mock<IDiskAccess>().Object };
            presenter.DeletePage(3);
            Assert.AreEqual(0, _site.GetRootPages().Count);
        }

        // Root
        //     Folder1 (F1)
        //         Page1 (P1)
        //     Folder2 (F2)
        //         Folder3 (F3)
        //             Folder5 (F5)
        //         Folder4 (F4)
        //             Page2 (A2)
        //         Folder1 (F6)
        //     Page1 (P3)

        [TestMethod]
        public void GetAllowedRootFolders_DefaulAccessDenied_NothingReturned()
        {
            SetupPermissionsTests();
            _admin.Group.DefaultPageAccess = DefaultAccess.Deny;
            IList<Folder> allowedFolders = _presenter.GetAllowedRootFolders();
            Assert.AreEqual(0, allowedFolders.Count);
        }

        [TestMethod]
        public void GetAllowedRootFolders_DefaulAccessAllowed_TwoFoldersReturned()
        {
            SetupPermissionsTests();
            _admin.Group.DefaultPageAccess = DefaultAccess.Allow;
            IList<Folder> allowedFolders = _presenter.GetAllowedRootFolders();
            Assert.AreEqual(2, allowedFolders.Count);
        }

        [TestMethod]
        public void GetAllowedRootFolders_DefaulAccessDeniedSubFolderPageAllowed_OneFolderReturned()
        {
            SetupPermissionsTests();
            _admin.Group.DefaultPageAccess = DefaultAccess.Deny;
            _page2.AllowedAdministratorGroups.Add(_admin.Group);
            IList<Folder> allowedFolders = _presenter.GetAllowedRootFolders();
            Assert.AreEqual(1, allowedFolders.Count);
            Assert.AreEqual(1, allowedFolders[0].SubFolders.Count);
        }

        private void SetupPermissionsTests()
        {
            Prepare();
            _presenter = new ExplorerPresenter(_viewMock.Object, FactoryMock.Object) { DiskAccess = new Mock<IDiskAccess>().Object };
        }
    }

}
